package controlmercancias.remoto;

import controlmercancias.dominio.Medida;
import controlmercancias.dominio.Sensor;
import controlmercancias.dominio.Tren;
import controlmercancias.dominio.Usuario;
import java.io.IOException;
import java.net.Socket;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import controlmercancias.util.SocketManager;
import java.util.Map;

/**
 *
 * @author Hasier
 */
public class ServidorTren implements Runnable {

    private SocketManager sm;
    private Tren trenActual;
    private Usuario usuarioActual;
    private Map<String, Tren> trenes;

    public ServidorTren(Socket s) throws IOException {
        sm = new SocketManager(s);
    }

    @Override
    public void run() {
        try {
            String linea, comando, usuario = "", clave = "";
            for (int estado = 0; estado < 3;) {
                linea = sm.read();
                if (linea == null) {
                    return;
                }
                StringTokenizer sTok = new StringTokenizer(linea, " ");
                comando = sTok.nextToken().toUpperCase();

                switch (estado) {
                    case 0:
                        if (comando.equals("USER")) {
                            try {
                                usuario = sTok.nextToken();
                                if ("".equals(usuario.trim())) {
                                    throw new NoSuchElementException();
                                }
                                //FIXME Comprobar usuario
                                usuario = usuario.trim();
                                sm.write("210 OK Bienvenido " + usuario);
                                estado = 1;
                            } catch (NoSuchElementException ex) {
                                sm.write("411 ERR Falta el nombre de usuario");
                            }
                        } else if (comando.equals("SALIR")) {
                            estado = 3;
                        } else {
                            sm.write("500 ERR Comando incorrecto");
                        }
                        break;

                    case 1:
                        if (comando.equals("PASS")) {
                            try {
                                clave = sTok.nextToken();
                                if ("".equals(clave.trim())) {
                                    throw new NoSuchElementException();
                                }
                                Usuario u = new Usuario(usuario, clave, null);
                                //FIXME Comprobar contraseña
                                if (usuario.equals(u.getNombre()) && clave.equals(u.getPassword())) {
                                    sm.write("211 OK Bienvenido al sistema");
                                    trenes = ServidorTrenDemon.loadTrenesParaUsuario(u);
                                    usuarioActual = u;
                                    trenActual = trenes.get(trenes.keySet().iterator().next());
                                    estado = 2;
                                } else {
                                    sm.write("412 ERR La clave es incorrecta");
                                }
                            } catch (NoSuchElementException ex) {
                                sm.write("413 ERR Falta la clave");
                            }
                        } else if (comando.equals("SALIR")) {
                            estado = 3;
                        } else {
                            sm.write("500 ERR Comando incorrecto");
                        }
                        break;

                    case 2:
                        if (comando.equals("LISTSENSOR")) {
                            sm.write("122 OK Lista de sensores");
                            for (Sensor s : trenActual.getSensores()) {
                                sm.write(s.toString());
                            }
                            sm.write("222 OK Lista finalizada");
                        } else if (comando.equals("HISTORICO")) {
                            String sensorDesconocido = "424 ERR Sensor desconocido";
                            int id;
                            if ((id = getSensorId(sTok, sensorDesconocido)) != -1) {
                                boolean ok = checkSensor(id, sensorDesconocido);
                                if (ok) {
                                    Sensor sensor = getSensor(id);
                                    sm.write("123 OK Lista de medidas");
                                    for (Medida m : sensor.getMedidas()) {
                                        sm.write(m.toString());
                                    }
                                    sm.write("222 OK Lista finalizada");
                                }
                            }
                        } else if (comando.equals("ON")) {
                            String sensorDesconocido = "427 ERR Sensor no existe";
                            int id;
                            if ((id = getSensorId(sTok, sensorDesconocido)) != -1) {
                                boolean ok = checkSensor(id, sensorDesconocido);
                                if (ok) {
                                    Sensor sensor = getSensor(id);
                                    if (sensor.isEstado()) {
                                        sm.write("428 ERR Sensor en estado ON");
                                    } else {
                                        sensor.setEstado(true);
                                        sm.write("213 OK Sensor activo");
                                    }
                                }
                            }
                        } else if (comando.equals("OFF")) {
                            String sensorDesconocido = "427 ERR Sensor no existe";
                            int id;
                            if ((id = getSensorId(sTok, sensorDesconocido)) != -1) {
                                boolean ok = checkSensor(id, sensorDesconocido);
                                if (ok) {
                                    Sensor sensor = getSensor(id);
                                    if (!sensor.isEstado()) {
                                        sm.write("429 ERR Sensor en estado OFF");
                                    } else {
                                        sensor.setEstado(false);
                                        sm.write("214 OK Sensor desactivado");
                                    }
                                }
                            }
                        } else if (comando.equals("ONGPS")) {
                            if(trenActual.isGpsConnected()) {
                                sm.write("429 ERR GPS en estado ON");
                            } else {
                                trenActual.setGpsConnected(true);
                                sm.write("215 OK GPS activado");
                            }
                        } else if (comando.equals("OFFGPS")) {
                            if(!trenActual.isGpsConnected()) {
                                sm.write("430 ERR GPS en estado OFF");
                            } else {
                                trenActual.setGpsConnected(true);
                                sm.write("216 OK GPS desactivado");
                            }
                        } else if (comando.equals("GET_VALACT")) {
                            String sensorDesconocido = "424 ERR Sensor desconocido";
                            int id;
                            if ((id = getSensorId(sTok, sensorDesconocido)) != -1) {
                                boolean ok = checkSensor(id, sensorDesconocido);
                                if (ok) {
                                    Sensor sensor = getSensor(id);
                                    if (!sensor.isEstado()) {
                                        sm.write("426 ERR Sensor en OFF");
                                    } else {
                                        sm.write("124 OK crear_medida"); //FIXME Crear medida
                                    }
                                }
                            }
                        } else if (comando.equals("GET_LOC")) {
                            sm.write("124 OK coordenada"); //FIXME Añadir coordenada
                        } else if (comando.equals("ACTUALIZAR")) {
                            if(trenActual.getEstado().equals(Tren.EstadoTren.MANTENIMIENTO)) {
                                //TODO Realizar la actualización
                                sm.write("216 OK Actualización completada");
                            } else {
                                sm.write("430 ERR Tren no estado mantenimiento");
                            }
                        } else if (comando.equals("SALIR")) {
                            estado = 3;
                        } else {
                            sm.write("500 ERR Comando incorrecto");
                        }
                        break;
                }
            }
            sm.write("218 OK Adios");
            sm.close();
            ServidorTrenDemon.trainServerFinished();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } catch (NoSuchElementException nsee) {
            nsee.printStackTrace();
        }
    }

    /**
     * Obtiene un sensor a partir de un id
     * @param id El id del sensor
     * @return El sensor, si es que existe. Si no, null
     */
    private Sensor getSensor(int idNum) {
        Sensor sensor = null;
        for (Sensor s : trenActual.getSensores()) {
            if (s.getId() == idNum) {
                sensor = s;
                break;
            }
        }
        return sensor;
    }

    /**
     * Comprueba un sensor
     * @param id El id del sensor
     * @return true si el sensor existe. Si ha ocurrido algún error, false
     * @throws IOException 
     */
    private boolean checkSensor(int idNum, String sensorDesconocido) throws IOException {
        try {
            Sensor sensor = getSensor(idNum);
            if (sensor == null) {
                throw new NumberFormatException();
            } else {
                return true;
            }
        } catch (NumberFormatException ex) {
            //sm.write("427 ERR Sensor no existe");
            sm.write(sensorDesconocido);
        }
        return false;
    }

    /**
     * Devuelve el id del sensor que contiene y al que apunta el StringTokenizer
     * @param sTok El StringTokenizer que contiene y apunta al id del sensor
     * @return El id del sensor o -1 si no es correcto
     * @throws IOException 
     */
    private int getSensorId(StringTokenizer sTok, String sensorDesconocido) throws IOException {
        try {
            String id = sTok.nextToken();
            if ("".equals(id.trim())) {
                throw new NoSuchElementException();
            }
            int idNum = Integer.parseInt(id);
            return idNum;
        } catch (NoSuchElementException ex) {
            sm.write("411 ERR Falta el parámetro id_sensor");
            return -1;
        } catch (NumberFormatException ex) {
            //sm.write("427 ERR Sensor no existe");
            sm.write(sensorDesconocido);
            return -1;
        }
    }
}
